﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Client.model
{
    public class Pitting : INotifyPropertyChanged
    { 
        int precision = 10;
        int graphWidth = 300;        
        bool isLeft;      
        bool isLeftRightSynchronous;
        bool isBasic = !true;
        bool isPointVisible;

        ObservableCollection<ControlPoint> leftPoints  = new ObservableCollection<ControlPoint>();
        ObservableCollection<ControlPoint> rightPoints = new ObservableCollection<ControlPoint>();        
        ObservableCollection<ControlLine> leftLines  = new ObservableCollection<ControlLine>();
        ObservableCollection<ControlLine> rightLines = new ObservableCollection<ControlLine>();

        ObservableCollection<ControlPoint> leftPoints95  = new ObservableCollection<ControlPoint>();
        ObservableCollection<ControlPoint> rightPoints95 = new ObservableCollection<ControlPoint>();
        ObservableCollection<ControlLine> leftLines95  = new ObservableCollection<ControlLine>();
        ObservableCollection<ControlLine> rightLines95 = new ObservableCollection<ControlLine>();

        ObservableCollection<ControlPoint> hearingLeftPoints = new ObservableCollection<ControlPoint>();
        ObservableCollection<ControlPoint> hearingRightPoints = new ObservableCollection<ControlPoint>();
        ObservableCollection<ControlLine> hearingLeftLines = new ObservableCollection<ControlLine>();
        ObservableCollection<ControlLine> hearingRightLines = new ObservableCollection<ControlLine>();
        
        public Pitting() {
            if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                IsPointVisible = true;
            }
        }

        internal void setHearingData(int[] leftData, int[] rightData, int maxY, int screenHeight)
        {
            hearingLeftPoints.Clear();
            hearingRightPoints.Clear();

            for (int i = 0; i < leftData.Length; i++)
            {
                hearingLeftPoints.Add(new ControlPoint((graphWidth / (leftData.Length - 1)) * i, leftData[i], maxY,screenHeight, true));
                hearingRightPoints.Add(new ControlPoint((graphWidth / (rightData.Length - 1)) * i, rightData[i], maxY, screenHeight,true));
            }

            LineDraw(hearingLeftLines, hearingLeftPoints);
            LineDraw(hearingRightLines, hearingRightPoints);
        }      

        public void setSideData(int[] data, bool isLeft, int linesIndex) {
            this.IsLeft = isLeft;

            ObservableCollection<ControlPoint> points = rightPoints;
            ObservableCollection<ControlLine> lines = rightLines;         

            if (!IsLeft && linesIndex==1) {
                points = rightPoints95;
                lines = rightLines95;             
            }
            else if(IsLeft && linesIndex==0) {
                points = leftPoints;
                lines = leftLines;
            }
            else if (IsLeft && linesIndex == 1)
            {
                points = leftPoints95;
                lines = leftLines95;
            }

            points.Clear();           

            for (int i = 0; i < data.Length; i++)
            {
                points.Add(new ControlPoint((graphWidth / (data.Length - 1)) * i, data[i]));
               
            }

            LineDraw(lines, points);
            
        }     

        public void GainLeftChange(int v) {
            GainChange(leftLines, leftPoints, v);

            if (isLeftRightSynchronous) GainChange(rightLines, rightPoints, v);            
        }

        public void GainRightChange(int v)
        {
            GainChange(rightLines, rightPoints, v);

            if (isLeftRightSynchronous) GainChange(leftLines, leftPoints, v);
        }

        public void GainChange(ObservableCollection<ControlLine> lines,ObservableCollection<ControlPoint> points, int v)
        {
            for (int i = 0; i < points.Count; i++)
                points[i].Y += v;

            LineDraw(lines, points);
        }

        internal void PointChange(int side, bool isUp, int index)
        {            
            int start = index ;
            int end = index + 0;
            
            int step = ControlPoint.step;
            if (isUp) step = -ControlPoint.step;
            if (side == 0)
            {
                for (int i = start; i <= end; i++)                
                    leftPoints[i].Y += step;
                LineDraw(leftLines, leftPoints);                
            }
            else if (side == 1)
            {
                for (int i = start; i <= end; i++)
                    rightPoints[i].Y += step;

                LineDraw(rightLines, rightPoints);
            }
            else
            {
                for (int i = start; i <= end; i++)
                {
                    leftPoints[i].Y += step;
                    rightPoints[i].Y += step;
                }
                LineDraw(leftLines, leftPoints);
                LineDraw(rightLines, rightPoints);
            }
        }

        internal void PointChange(int side, bool isUp, int index, double sliderValue)
        {
            int start = 0;  
            int end = leftPoints.Count-1;
            int step = (int)sliderValue;
            
            if (side == 0)
            {
                for (int i = start; i <= end; i++)
                {
                    leftPoints[i].YSlide = step;
                    leftPoints95[i].YSlide = step;
                }
                LineDraw(leftLines, leftPoints);
                LineDraw(leftLines95, leftPoints95);
            }
            else if (side == 1)
            {
                for (int i = start; i <= end; i++)
                {
                    rightPoints[i].YSlide = step;
                    rightPoints95[i].YSlide = step;
                }

                LineDraw(rightLines, rightPoints);
                LineDraw(rightLines95, rightPoints95);
            }
            else
            {
                for (int i = start; i <= end; i++)
                {
                    leftPoints[i].YSlide = step;
                    leftPoints95[i].YSlide = step;
                    rightPoints[i].YSlide = step;
                    rightPoints95[i].YSlide = step;
                }
                LineDraw(leftLines, leftPoints);
                LineDraw(leftLines95, leftPoints95);
                LineDraw(rightLines, rightPoints);
                LineDraw(rightLines95, rightPoints95);
            }            
        }
       
        private void LineDraw(ObservableCollection<ControlLine> lines,ObservableCollection<ControlPoint> points)
        {
            lines.Clear();           
            CubicSpline(lines, points);          

            NotifyPropertyChanged("LeftLines"); 
            NotifyPropertyChanged("LeftPoints");
            NotifyPropertyChanged("RightLines");
            NotifyPropertyChanged("RightPoints");
        }

        void CubicSpline(ObservableCollection<ControlLine> lines, ObservableCollection<ControlPoint> points)
        {
            int np = points.Count;
            if (np <= 0) return;

            float[] yCoords = new float[np];        // Newton form coefficients
            float[] xCoords = new float[np];        // x-coordinates of nodes            
            float oldy = 0;
            float oldx = 0;
            int npp = np * precision;          // number of points used for drawing

            // draw the control points
            for (int i = 0; i < np; i++)
            {
                xCoords[i] = points[i].X;
                yCoords[i] = points[i].Y0;
            }

            float[] a = new float[np];
            float x1;
            float x2;
            float[] h = new float[np];
            for (int i = 1; i <= np - 1; i++)
            {
                h[i] = xCoords[i] - xCoords[i - 1];
            }

            if (np > 2)
            {
                float[] sub = new float[np - 1];
                float[] diag = new float[np - 1];
                float[] sup = new float[np - 1];

                for (int i = 1; i <= np - 2; i++)
                {
                    diag[i] = (h[i] + h[i + 1]) / 3;
                    sup[i] = h[i + 1] / 6;
                    sub[i] = h[i] / 6;
                    a[i] = (yCoords[i + 1] - yCoords[i]) / h[i + 1] - (yCoords[i] - yCoords[i - 1]) / h[i];
                }
                solveTridiag(sub, diag, sup, ref a, np - 2);
            }
            // note that a[0]=a[np-1]=0

            oldx = xCoords[0];
            oldy = yCoords[0];

            lines.Add(new ControlLine((int)oldx, (int)oldy, (int)oldx, (int)oldy));

            for (int i = 1; i <= np - 1; i++)
            {   // loop over intervals between nodes
                for (int j = 1; j <= precision; j++)
                {
                    x1 = (h[i] * j) / precision;
                    x2 = h[i] - x1;
                    float y = ((-a[i - 1] / 6 * (x2 + h[i]) * x1 + yCoords[i - 1]) * x2 +
                        (-a[i] / 6 * (x1 + h[i]) * x2 + yCoords[i]) * x1) / h[i];
                    float x = xCoords[i - 1] + x1;

                    lines.Add(new ControlLine((int)oldx, (int)oldy, (int)x, (int)y));

                    oldx = x;
                    oldy = y;
                }
            }
        }
     
        private void solveTridiag(float[] sub, float[] diag, float[] sup, ref float[] b, int n)
        {
            /*                  solve linear system with tridiagonal n by n matrix a
                                using Gaussian elimination *without* pivoting
                                where   a(i,i-1) = sub[i]  for 2<=i<=n
                                        a(i,i)   = diag[i] for 1<=i<=n
                                        a(i,i+1) = sup[i]  for 1<=i<=n-1
                                (the values sub[1], sup[n] are ignored)
                                right hand side vector b[1:n] is overwritten with solution 
                                NOTE: 1...n is used in all arrays, 0 is unused */
            int i;
            /*                  factorization and forward substitution */
            for (i = 2; i <= n; i++)
            {
                sub[i] = sub[i] / diag[i - 1];
                diag[i] = diag[i] - sub[i] * sup[i - 1];
                b[i] = b[i] - sub[i] * b[i - 1];
            }
            b[n] = b[n] / diag[n];
            for (i = n - 1; i >= 1; i--)
            {
                b[i] = (b[i] - sup[i] * b[i + 1]) / diag[i];
            }
        }

        public ObservableCollection<ControlPoint> LeftPoints
        {
            get { return leftPoints; }
            set { leftPoints = value; NotifyPropertyChanged("LeftPoints"); }
        }

        public ObservableCollection<ControlPoint> RightPoints
        {
            get { return rightPoints; }
            set { rightPoints = value; }
        }

        public ObservableCollection<ControlLine> LeftLines
        {
            get { return leftLines; }
            set { leftLines = value; NotifyPropertyChanged("LeftLines"); }
        }

        public ObservableCollection<ControlLine> RightLines
        {
            get { return rightLines; }
            set { rightLines = value; }
        }

        public ObservableCollection<ControlPoint> LeftPoints95
        {
            get { return leftPoints95; }
            set { leftPoints95 = value; }
        }

        public ObservableCollection<ControlPoint> RightPoints95
        {
            get { return rightPoints95; }
            set { rightPoints95 = value; }
        }

        public ObservableCollection<ControlLine> LeftLines95
        {
            get { return leftLines95; }
            set { leftLines95 = value; }
        }

        public ObservableCollection<ControlLine> RightLines95
        {
            get { return rightLines95; }
            set { rightLines95 = value; }
        } 

        public bool IsLeftRightSynchronous
        {
            get { return isLeftRightSynchronous; }
            set { isLeftRightSynchronous = value; }
        }

        public bool IsBasic
        {
            get { return isBasic; }
            set { isBasic = value; NotifyPropertyChanged("IsBasic"); }
        }

        public bool IsPointVisible
        {
            get { return isPointVisible; }
            set { isPointVisible = value; NotifyPropertyChanged("IsPointVisible"); }
        }

        public bool IsLeft
        {
            get { return isLeft; }
            set { isLeft = value; }
        }

        public ObservableCollection<ControlPoint> HearingLeftPoints
        {
            get { return hearingLeftPoints; }
            set { hearingLeftPoints = value; }
        }

        public ObservableCollection<ControlPoint> HearingRightPoints
        {
            get { return hearingRightPoints; }
            set { hearingRightPoints = value; }
        }

        public ObservableCollection<ControlLine> HearingLeftLines
        {
            get { return hearingLeftLines; }
            set { hearingLeftLines = value; }
        }

        public ObservableCollection<ControlLine> HearingRightLines
        {
            get { return hearingRightLines; }
            set { hearingRightLines = value; }
        }


        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        
    }
}
